<!-- 基础表单组件 -->

<!-- 属性 -->
<!-- 配置名称 | 类型 | 用途 | 返回值 | 默认值 | 必填 | 说明 -->
<!-- form        | Record<string, any> | 表单数据     | - | -    | * | 无 -->
<!-- rules       | FormRules           | 表单校验规则 | - | {}   | - | 无 -->
<!-- width       | string              | 表单宽度     | - | 100% | - | 无 -->
<!-- attributes  | Object              | 其他属性     | - | {}   | - | 无 -->

<!-- 事件 -->
<!-- 事件名 | 说明 | Type -->
<!-- 无 -->

<!-- 插槽 -->
<!--
  #default: 各表单项
-->

<!-- expose -->
<!-- 方法名 | 说明 | Type -->
<!-- submit | 表单提交方法（包含校验方法调用，及表单格式化） | (format?: () => Record<string, any>) => Promise<Record<string, any> | null> -->
<script setup lang="ts">
import { ref, watch } from 'vue'
import type { FormRules } from 'element-plus'

interface Props {
  form: Record<string, any>
  rules?: FormRules
  width?: string
  attributes?: any
}

const { form, rules = {}, width = '100%', attributes = {} } = defineProps<Props>()

const bindAttributes = {}
Object.assign(bindAttributes, attributes)

const bindWidth = ref(width || '100%')

const formRef = ref()

const formRules = ref<FormRules>({})

const validate = (callback: any) => {
  formRef.value.validate((valid: boolean) => {
    callback(valid)
  })
}

const submit = (format?: () => Record<string, any>) => {
  return new Promise<Record<string, any> | null>((resolve) => {
    formRef.value.validate((valid: boolean) => {
      let res: Record<string, any> | null = null
      if (valid) {
        res = (format && format()) || form
      }
      resolve(res)
    })
  })
}

const calling = (method: string, ...args: any) => {
  if (typeof formRef.value[method] === 'function') {
    return formRef.value[method](...args)
  }
  if (method === 'fields') {
    return formRef.value.fields
  }
  return void 0
}

watch(
  () => rules,
  (newVal) => {
    if (newVal) {
      formRules.value = newVal
    }
  },
  {
    deep: true,
    immediate: true,
  },
)

watch(
  () => width,
  (newVal) => {
    bindWidth.value = newVal || '100%'
  },
  {
    immediate: true,
  },
)

defineExpose({
  validate,
  submit,
  calling,
})
</script>

<template>
  <div class="base-form-wrapper">
    <el-form ref="formRef" :model="form" :rules="formRules" v-bind="bindAttributes" @submit.prevent>
      <slot></slot>
    </el-form>
  </div>
</template>

<style lang="scss" scoped>
.base-form-wrapper {
  width: v-bind(bindWidth);
  :deep(.el-form-item) {
    margin-bottom: 24px;
  }
}
</style>
